package model

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/go-redis/redis/v8"
	"strconv"
	"time"

	_ "github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"gorm.io/gorm"
)

type CustRecords struct {
	Id       int64     `gorm:"primary;type:int(11)"`
	CustId   string    `gorm:"type:varchar(20);not null"`
	SendTime time.Time `gorm:"type:datetime;not null"`
	SendFlag uint8     `gorm:"type:tinyint(4)"`
}

type CustomerCount struct {
	CustId string
	Count  int64
}

func (t *CustomerCount) MarshalBinary() ([]byte, error) {
	// 实现你的序列化逻辑，通常使用 JSON 或 gob 等
	return json.Marshal(t)
}

// get records times in 30 days
func GetSendCntWithRedis(tx *gorm.DB, custId string) (int64, error) {
	var count int64
	t := time.Now()
	// 查询从 time - 0day 到 time - 30day的数据 优先redis
	for day := 0; day <= 30; day++ {
		tt := t.AddDate(0, 0, -1*day)
		cnt, err := GetCntByUID(tx, custId, tt)
		if err != nil {
			return 0, err
		}
		count += cnt
	}
	return count, nil
}

func GetSendCnt(tx *gorm.DB, custId string) (int64, error) {
	var count int64
	if err := tx.Raw("SELECT COUNT(*) AS count FROM cust_records WHERE cust_id = ? AND datediff(now(), send_time) <= 30 GROUP BY cust_id ORDER BY cust_id", custId).First(&count).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return 0, nil
		} else {
			return 0, err
		}
	}
	return count, nil
}

// add record to user
func AddRecord(tx *gorm.DB, custId string) error {
	if err := tx.Create(&CustRecords{CustId: custId, SendTime: time.Now()}).Error; err != nil {
		return err
	}
	key := "customer:cnt:" + time.Now().Format("2006-01-02")
	if _, err := RDB.HIncrBy(Ctx, key, custId, 1).Result(); err != nil {
		return err
	}
	return nil
}

// get ids and cnts of specific day
func GetCntByDate(tx *gorm.DB, res *[]CustomerCount, t time.Time) error {
	if err := tx.Debug().Raw("SELECT cust_id, count(*) AS count FROM cust_records WHERE send_time >= ? AND send_time < ? GROUP BY cust_id", t.Format("2006-01-02"), t.Add(24*time.Hour).Format("2006-01-02")).Find(res).Error; err != nil {
		return err
	}
	return nil
}

func GetCnt(tx *gorm.DB, res *[]CustomerCount, t time.Time) error {
	// 先查询redis
	key := "customer:cnt:" + t.Format("2006-01-02")
	exists, err := RDB.Exists(Ctx, key).Result()
	if err != nil {
		return err
	}
	// redis 中存在
	if exists > 0 {
		result, err := RDB.HGetAll(Ctx, key).Result()
		if err != nil {
			return err
		}
		tmp := make([]CustomerCount, 0)
		for key, val := range result {
			intVal, err := strconv.ParseInt(val, 10, 64)
			if err != nil {
				return err
			}
			tmp = append(tmp, CustomerCount{key, intVal})
		}
		res = &tmp
		return nil
	}
	if err := tx.Debug().Raw("SELECT cust_id, count(*) as count FROM cust_records WHERE send_time >= ? AND send_time < ? GROUP BY cust_id", t.Format("2006-01-02"), t.Add(24*time.Hour).Format("2006-01-02")).Find(res).Error; err != nil {
		return err
	}
	// 同步到redis
	hashData := make(map[string]string)
	hashData["Empty"] = "0"
	for _, item := range *res {
		hashData[item.CustId] = strconv.FormatInt(item.Count, 10)
	}
	pipe := RDB.TxPipeline()
	pipe.HSet(Ctx, key, hashData)
	pipe.Expire(Ctx, key, time.Hour*30*24)
	_, err = pipe.Exec(Ctx)
	if err != nil {
		return err
	}
	return nil
}

func GetCntByUID(tx *gorm.DB, custId string, t time.Time) (int64, error) {
	// 先查询redis
	key := "customer:cnt:" + t.Format("2006-01-02")
	result, err := RDB.Exists(Ctx, key).Result()
	if err != nil {
		return 0, err
	}
	if result > 0 {
		result, err := RDB.HGet(Ctx, key, custId).Result()
		if err != nil {
			if err == redis.Nil {
				return 0, nil
			}
			return 0, err
		}
		tmp, _ := strconv.ParseInt(result, 10, 64)
		return tmp, nil
	}
	fmt.Println("111")

	res := &[]CustomerCount{}
	if err := tx.Debug().Raw("SELECT cust_id, count(*) as count FROM cust_records WHERE send_time >= ? AND send_time < ? GROUP BY cust_id", t.Format("2006-01-02"), t.Add(24*time.Hour).Format("2006-01-02")).Find(res).Error; err != nil {
		return 0, err
	}
	// 同步到redis
	hashData := make(map[string]string)
	hashData["Empty"] = "0"
	for _, item := range *res {
		hashData[item.CustId] = strconv.FormatInt(item.Count, 10)
	}
	pipe := RDB.TxPipeline()
	pipe.HSet(Ctx, key, hashData)
	pipe.Expire(Ctx, key, time.Hour*30*24)
	_, err = pipe.Exec(Ctx)
	if err != nil {
		return 0, err
	}
	tmp, _ := strconv.ParseInt(hashData[custId], 10, 64)
	return tmp, nil
}
